//origin: ukui(-panel):sfinae.cpp
#include <stdio.h>

//模板类：用于判断是否为指针类型
template <class T>
struct is_pointer
{
    //函数模板，匹配除类成员指针外的任意指针类型参数(包括函数指针)
    template <class U>
    static char is_ptr(U *); 

#ifndef NO_MEMBER_PTR
    //重载函数模板：匹配类成员指针
    template <class X, class Y> 
    static char is_ptr(Y X::*); //类成员指针
#endif //NO_MEMBER_PTR

#if 0 //通用指针已经包含函数指针了，因此这里无需单独匹配函数指针
    template <class U>
    static char is_ptr(U (*)()); //返回值为U的函数类型指针
#endif
    //重载函数：匹配任意类型参数，优先级低(实际匹配非指针参数情况)
    static double is_ptr(...); //其他情况(非指针)

    static T t;
    //T: 为指针类型, 则value为1, 否则为0
    enum { value = sizeof(is_ptr(t)) == sizeof(char) };
};

struct Foo {
    int bar;
    void f();
};

void testTypeCheck() {
    typedef int * IntPtr;
    typedef int Foo::* FooMemberPtr;
    typedef void Func(); 
    typedef Func Foo::* FooMemberFuncPtr;
    typedef Func *FuncPtr;
    typedef void (*FuncPtr2)(int);

    printf("%d\n",is_pointer<IntPtr>::value);        // prints 1
    printf("%d\n",is_pointer<FooMemberPtr>::value);  // prints 1
    printf("%d\n",is_pointer<FooMemberFuncPtr>::value);  // prints 1
    printf("%d\n",is_pointer<FuncPtr>::value);       // prints 1
    printf("%d\n",is_pointer<FuncPtr2>::value);       // prints 1
}

//类模板：判断T类是否定义了no_destroy成员(数据或函数)
template<typename T>
struct has_no_destroy {
    //函数模板：参数类型为类成员指针
    template<typename C>
    static char test(decltype(&C::no_destroy));

    //重载函数模板：匹配任意参数类型，优先级低(实际为无no_destroy时匹配)
    template<typename C>
    static int test(...);
    //T类定义了no_destroy成员，则value为true(1), 否则false(0)
    const static bool value = sizeof(test<T>(0)) == 1;
};

struct A {

};

struct B {
    void no_destroy(){}
};
struct C {
    int no_destroy;
};

struct D : B {

};

void testNoDestroy() {
    printf("%d\n",has_no_destroy<A>::value);
    printf("%d\n",has_no_destroy<B>::value);
    printf("%d\n",has_no_destroy<C>::value);
    printf("%d\n",has_no_destroy<D>::value);
}

int main(void)
{
    puts("testTypeCheck");
    testTypeCheck();
    puts("testNoDestroy");
    testNoDestroy();
    return 0;
}
